﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MandelbrotLib
{
    public class MandelbrotSet
    {
        protected int[,] mPixels;
        
        protected RealNumber mX0;
        protected RealNumber mY0;
        protected RealNumber mX1;
        protected RealNumber mY1;

        protected RealNumber mRx;
        protected RealNumber mRy;

        protected int mGx;
        protected int mGy;

        protected RealNumber mGx_Recip;
        protected RealNumber mGy_Recip;

        protected int mMaxIterations;
        protected int mPrecision;

        public int this[int i, int j]
        {
            get
            {
                return mPixels[i, j];
            }
        }

        public int Precision
        {
            get
            {
                return mPrecision;
            }
        }

        public MandelbrotSet()
        {
            // -2 - 2j, 2 + 2j, g = 100
            SetRegion(RealNumber.CreateByNumerator(-2),
                RealNumber.CreateByNumerator(-2),
                RealNumber.CreateByNumerator(2),
                RealNumber.CreateByNumerator(2), 
                100, 100);

            SetNumberOfIterations(100);
        }

        public void SetRegion(RealNumber x0, RealNumber y0, 
            RealNumber x1, RealNumber y1, int gx, int gy)
        {
            x0.Renorm();
            y0.Renorm();
            x1.Renorm();
            y1.Renorm();

            mX0 = x0;
            mY0 = y0;
            mX1 = x1;
            mY1 = y1;

            mGx = gx;
            mGy = gy;
            mGx_Recip = RealNumber.CreateByDenominator(gx);
            mGy_Recip = RealNumber.CreateByDenominator(gy);

            mRx = mX1.Sub(mX0);
            mRy = mY1.Sub(mY0);

            mPrecision  = 0;
            mPrecision = Math.Max(mPrecision, x0.ChunksAfterDecimalPoint);
            mPrecision = Math.Max(mPrecision, y0.ChunksAfterDecimalPoint);
            mPrecision = Math.Max(mPrecision, x1.ChunksAfterDecimalPoint);
            mPrecision = Math.Max(mPrecision, y1.ChunksAfterDecimalPoint);
            //mPrecision += (mPrecision + 1) >> 1;

            mPixels = new int[mGx, mGy];
        }

        public void SetNumberOfIterations(int n)
        {
            mMaxIterations = n;
        }

        public void Calculate()
        {
            RealNumber threshold = RealNumber.CreateByNumerator(4);

            RealNumber x, y;
            int i, j;
            int k;


            for (i = 0; i < mGx; i++)
            {
                // x = mX0 + i * mRx / mGx

                x = mRx.Mul(RealNumber.CreateByNumerator(i));
                x = x.Mul(mGx_Recip);
                x = x.Add(mX0);

                for (j = 0; j < mGy; j++)
                {
                    y = mRy.Mul(RealNumber.CreateByNumerator(j));
                    y = y.Mul(mGy_Recip);
                    y = y.Add(mY0);

                    ComplexNumber c = new ComplexNumber(x, y);
                    ComplexNumber z = ComplexNumber.Zero;

                    for (k = 0; k < mMaxIterations; k++)
                    {
                        if (z.SquareLength.AbsCompareTo(threshold) >= 0)
                        {
                            break;
                        }
                        z = z.Mul(z);
                        z = z.Add(c);
                        z.RoundTo(mPrecision);
                    }

                    this.mPixels[i, j] = k;
                }
            }
        }
    }
}
